home *** CD-ROM | disk | FTP | other *** search
/ Gold Medal Software 3 / Gold Medal Software - Volume 3 (Gold Medal) (1994).iso / os2 / ssaver2x.arj / SAMPLE.C < prev    next >
C/C++ Source or Header  |  1993-12-13  |  17KB  |  512 lines

  1. /*
  2.     sample.c
  3.     sample saver module C source file version 1.1
  4.     (C) 1993 Siegfried Hanisch
  5. */
  6.  
  7. // changes since version 1.0 are marked with "!!!!!"
  8. // some typecasts (PSZ) were added to avoid compiler warnings. they are
  9. // not marked as changes
  10.  
  11. #define INCL_DOS
  12. #define INCL_DOSERRORS    // !!!!! this line added since version 1.0
  13. #define INCL_WIN
  14. #define INCL_GPI
  15. #include <os2.h>
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <string.h>
  19. #include <limits.h>
  20. #include <process.h>    // !!!!! this line added since version 1.0
  21.  
  22. #include "sample.h"
  23.  
  24.  
  25. // ===== preprocessor definitions
  26.  
  27. #define MODULEVERSION        0x00010001    // !!!!! changed
  28. #define STACKSIZE        32000
  29. #define SAVER_NAME_MAXLEN    32
  30. #define FUNCTION_CONFIGURE    1
  31. #define FUNCTION_STARTSAVER    2
  32. #define FUNCTION_STOPSAVER    3
  33. #define FUNCTION_QUERYNAME    4
  34. #define FUNCTION_QUERYENABLED    5
  35. #define FUNCTION_SETENABLED    6
  36. /*
  37.     $$$$$ insert code here $$$$$
  38.     This is the place for your preprocessor definitions.
  39.  
  40.     $$$$$ for example $$$$$
  41. #define CONFIGURATION_MINIMUM_COUNT 1
  42. #define CONFIGURATION_DEFAULT_COUNT 32
  43. #define CONFIGURATION_MAXIMUM_COUNT 100
  44. */
  45.  
  46.  
  47. // ===== prototypes
  48.  
  49. // !!!!! some of the prototypes changed since version 1.0
  50. // !!!!! following line added since version 1.0
  51. void    EXPENTRY SAVER_PROC(int function, HAB _hab, HWND hwndOwner, char *appname, void *buffer);
  52. static    MRESULT EXPENTRY SaverWindowProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2);
  53. static    MRESULT    EXPENTRY ConfigureDlgProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2);
  54. #if defined(__IBMC__)
  55. static    void    _Optlink draw_thread(void *args);
  56. static    void    _Optlink priority_thread(void *args);
  57. #elif defined(__BORLANDC__)
  58. static    void    _USERENTRY draw_thread(void *args);
  59. static    void    _USERENTRY priority_thread(void *args);
  60. #else
  61. static    void    draw_thread(void *args);
  62. static    void    priority_thread(void *args);
  63. #endif
  64. static    void    load_configuration_data(void);
  65.  
  66.  
  67. // ===== global data
  68.  
  69. static    LONG    screenSizeX = 0;        // screen size x
  70. static    LONG    screenSizeY = 0;        // screen size y
  71. static    HWND    hwndSaver = NULLHANDLE;        // saver window handle
  72. static    HMODULE    hmodDLL = NULLHANDLE;        // saver module dll handle
  73. static    char    *application_name;        // name of ScreenSaver app
  74. static    TID    tidDraw;            // drawing-thread ID
  75. static    HPS    hps;                // presentation space handle
  76. static    HAB    hab;                // anchor block handle
  77. static    BOOL    low_priority = TRUE;        // low-priority flag
  78. static    BOOL    configuration_data_loaded = FALSE; // config data loaded flag
  79. static    volatile BOOL stop_draw_thread;        // stop flag
  80. static    char    modulename[SAVER_NAME_MAXLEN+1]; // module name buffer
  81.  
  82. static    struct    _configuration_data {
  83.     ULONG    version;
  84.     BOOL    enabled;
  85. /*
  86.     $$$$$ insert code here $$$$$
  87.     If your saver module needs some additional configuration data,
  88.     insert it here. It is automatically loaded and saved.
  89.  
  90.     $$$$$ for example $$$$$
  91.     int    count;
  92. */
  93. } configuration_data;
  94.  
  95.  
  96. // ===== code
  97.  
  98. /*
  99.     SAVER_PROC
  100.     This is the entry point into the saver module that is called by
  101.     the ScreenSaver program.
  102.     There should be no reason to alter the code.
  103.     Depending on the requested function, the following tasks
  104.     are performed:
  105.     * call the configuration dialog of the saver module
  106.     * copy the name of the saver module into the supplied buffer
  107.     * tell if the saver module is enabled
  108.     * set the "enabled" state of the saver module
  109.     * start the saver
  110.     * stop the saver
  111.     Note that before any processing is done, module configuration data is
  112.     loaded from the INI-files.
  113. */
  114. // !!!!! the function declaration changed since version 1.0 (EXPENTRY added)
  115. void    EXPENTRY SAVER_PROC(int function, HAB _hab, HWND hwndOwner, char *appname, void *buffer)
  116. {
  117. // !!!!! the next 4 lines were added since version 1.0
  118. #if defined(__BORLANDC__)
  119.     extern ULONG _os2hmod;
  120.     hmodDLL = _os2hmod;
  121. #endif
  122.     hab = _hab;
  123.     application_name = appname;
  124.     // load all configuration data from INI-file
  125.     load_configuration_data();
  126.     switch(function){
  127.     case FUNCTION_CONFIGURE:
  128.         // call the configuration dialog
  129.         WinDlgBox(HWND_DESKTOP, hwndOwner, ConfigureDlgProc,
  130.           hmodDLL, IDD_CONFIGURE, application_name);
  131.         return;
  132.     case FUNCTION_STARTSAVER:
  133.         // start the saver
  134.         // get "low priority" state from supplied buffer (BOOL *)
  135.         low_priority = *((BOOL *)buffer);
  136.         // random seed
  137.         srand(WinGetCurrentTime(hab));
  138.         // query size of screen
  139.         screenSizeX = WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN);
  140.         screenSizeY = WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN);
  141.         // register window class for the saver window
  142.         WinRegisterClass(hab, (PSZ)modulename,
  143.           (PFNWP)SaverWindowProc, 0, 0);
  144.         // create the saver window
  145.         hwndSaver = WinCreateWindow(HWND_DESKTOP, (PSZ)modulename,
  146.           (PSZ)NULL, WS_VISIBLE, 0, 0, screenSizeX, screenSizeY,
  147.           HWND_DESKTOP, HWND_TOP, 0, NULL, NULL);
  148.         return;
  149.     case FUNCTION_STOPSAVER:
  150.         // stop the saver
  151.         if(hwndSaver != NULLHANDLE){
  152.             // move saver window to front
  153.             WinSetWindowPos(hwndSaver, HWND_TOP,
  154.               0, 0, 0, 0, SWP_ZORDER);
  155.             // destroy saver window
  156.             WinDestroyWindow(hwndSaver);
  157.             hwndSaver = NULLHANDLE;
  158.         }
  159.         return;
  160.     case FUNCTION_QUERYNAME:
  161.         // copy module name to supplied buffer (CHAR *)
  162.         strcpy(buffer, modulename);
  163.         return;
  164.     case FUNCTION_QUERYENABLED:
  165.         // copy "enabled" state to supplied buffer (BOOL *)
  166.         *((BOOL *)buffer) = configuration_data.enabled;
  167.         return;
  168.     case FUNCTION_SETENABLED:
  169.         // get new "enabled" state from supplied buffer (BOOL *)
  170.         configuration_data.enabled = *((BOOL *)buffer);
  171.         PrfWriteProfileData(HINI_USER, (PSZ)application_name, (PSZ)modulename, (PSZ)&configuration_data, sizeof(configuration_data));
  172.         return;
  173.     }
  174.  
  175.     // illegal function request
  176.     WinAlarm(HWND_DESKTOP, WA_ERROR);
  177.     return;
  178. }
  179.  
  180. // !!!!! the #if and #endif below were added since version 1.0
  181. #if !defined(__BORLANDC__)
  182. /*
  183.     _DLL_InitTerm
  184.     This procedure is called at DLL initialization and termination.
  185.     There should be no reason to alter the code.
  186. */
  187. ULONG    _DLL_InitTerm(HMODULE hmod, ULONG flag)
  188. {
  189.     switch(flag){
  190.     case 0:    // initializing DLL
  191.         hmodDLL = hmod;
  192.         return 1;
  193.     case 1: // terminating DLL
  194.         return 1;
  195.     default:
  196.         // return error
  197.         return 0;
  198.     }
  199. }
  200. #endif
  201.  
  202. /*
  203.     ConfigureDlgProc
  204.     This is the dialog procedure for the module configuration dialog.
  205.     The dialog contains a check box for enabling/disabling the module
  206.     and two push buttons ("OK" and "Cancel") to close/cancel the dialog.
  207.     This is enough for simple saver modules, but can easily be expanded
  208.     for more saver modules that need more settings.
  209. */
  210. MRESULT    EXPENTRY ConfigureDlgProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  211. {
  212.     char    buf[sizeof(modulename)+20];
  213.     static    HWND    hwndEnabled;
  214. /*
  215.     $$$$$ insert code here $$$$$
  216.     If you need additional data for dialog processing, insert it here.
  217.  
  218.     $$$$$ for example $$$$$
  219.     static    HWND    hwndCount;
  220. */
  221.     switch(msg){
  222.     case WM_INITDLG:
  223.         // set titlebar of the dialog window
  224.         // to "MODULENAME configuration"
  225.         strcpy(buf, modulename);
  226.         strcat(buf, " configuration");
  227.         WinSetWindowText(hwnd, (PSZ)buf);
  228.  
  229.         // get window handles of the dialog controls
  230.         // and set initial state of the controls
  231.         hwndEnabled = WinWindowFromID(hwnd, IDC_ENABLED);
  232.         WinSendMsg(hwndEnabled, BM_SETCHECK,
  233.           MPFROMSHORT(configuration_data.enabled), MPVOID);
  234. /*
  235.         $$$$$ insert code here $$$$$
  236.         Get window handles of your dialog controls.
  237.         Set initial state of your controls.
  238.  
  239.         $$$$$ for example $$$$$
  240.         hwndCount = WinWindowFromID(hwnd, IDC_COUNT);
  241.         WinSendMsg(hwndCount, SPBM_SETLIMITS, (MPARAM)CONFIGURATION_MAXIMUM_COUNT, (MPARAM)CONFIGURATION_MINIMUM_COUNT);
  242.         WinSendMsg(hwndCount, SPBM_SETCURRENTVALUE, MPFROMSHORT(configuration_data.count), MPVOID);
  243. */
  244.         // return FALSE since we did not change the focus
  245.         return (MRESULT)FALSE;
  246.     case WM_COMMAND:
  247.         switch(SHORT1FROMMP(mp1)){
  248.         case IDC_OK:
  249.             // OK button was pressed. query the control settings
  250.             configuration_data.enabled = SHORT1FROMMR(WinSendMsg(hwndEnabled, BM_QUERYCHECK, MPVOID, MPVOID));
  251. /*
  252.             $$$$$ insert code here $$$$$
  253.             Query control settings of your controls.
  254.  
  255.             $$$$$ for example $$$$$
  256.             WinSendMsg(hwndCount, SPBM_QUERYVALUE, MPFROMP(&configuration_data.count), MPFROM2SHORT(0, SPBQ_DONOTUPDATE));
  257. */
  258.             // write all configuration data to INI-file
  259.             PrfWriteProfileData(HINI_USER, (PSZ)application_name, (PSZ)modulename, (PSZ)&configuration_data, sizeof(configuration_data));
  260.             // end dialog
  261.             WinDismissDlg(hwnd, TRUE);
  262.             return (MRESULT)0;
  263.         case IDC_CANCEL:
  264.             // dialog was cancelled; end it
  265.             WinDismissDlg(hwnd, FALSE);
  266.             return (MRESULT)0;
  267.         default:
  268.             return (MRESULT)0;
  269.         }
  270.     }
  271.     return WinDefDlgProc(hwnd, msg, mp1, mp2);
  272. }
  273.  
  274. /*
  275.     SaverWindowProc
  276.     This is the window procedure of the screen-size window that is
  277.     created when the saver starts.
  278.     There should be no reason to alter the code.
  279.     Note that we do not process WM_PAINT messages. They are forwarded to
  280.     the default window procedure, which just validates the window area
  281.     and does no drawing. All drawing to the window should be done in
  282.     the drawing-thread. Therefore, if you want to blank the screen before
  283.     drawing on it for instance, issue a WinFillRect call at the beginning
  284.     of your drawing-thread.
  285. */
  286. MRESULT EXPENTRY SaverWindowProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  287. {
  288.     // !!!!! next line added since version 1.0
  289.     static    TID    tidPriority;
  290.     switch(msg){
  291.     case WM_CREATE:
  292.         // reset the "stop" flag
  293.         stop_draw_thread = FALSE;
  294.         // store window handle
  295.         hwndSaver = hwnd;
  296.         // get presentation space
  297.         hps = WinGetPS(hwnd);
  298.         // start the drawing-thread
  299. /*
  300.         $$$$$ note $$$$$
  301.         Some compilers use another parameter ordering for
  302.         _beginthread. The _beginthread call below works with EMX,
  303.         ICC and BCC. Check your compiler docs for other compilers.
  304. */
  305. // !!!!! code for Borland C++ added since version 1.0
  306. #if defined(__BORLANDC__)
  307.         // for Borland C++
  308.         tidDraw = _beginthread(draw_thread, STACKSIZE, NULL);
  309. #elif defined(__EMX__) || defined(__IBMC__)
  310.         // for EMX and ICC
  311.         tidDraw = _beginthread(draw_thread, NULL, STACKSIZE, NULL);
  312. #endif
  313.  
  314.         // !!!!! next 3 lines added since version 1.0, some code deleted
  315.         // create thread to control priority of drawing thread
  316.         if(low_priority)
  317.             DosCreateThread(&tidPriority, (PFNTHREAD)priority_thread, 0, 2L, 1000);
  318.         return (MRESULT)FALSE;
  319.     case WM_DESTROY:
  320. // !!!!! most of the code for WM_DESTROY changed since version 1.0
  321.         if(low_priority)
  322.             DosKillThread(tidPriority);
  323.         // tell drawing-thread to stop
  324.         stop_draw_thread = TRUE;
  325.         if(DosWaitThread(&tidDraw, DCWW_NOWAIT) == ERROR_THREAD_NOT_TERMINATED){
  326.             // if priority of drawing-thread was set to idle time
  327.             // priority, set it back to normal value
  328.             DosSetPriority(PRTYS_THREAD, PRTYC_REGULAR, PRTYD_MAXIMUM, tidDraw);
  329.             // wait until drawing-thread has ended
  330.             DosWaitThread(&tidDraw, DCWW_WAIT);
  331.         }
  332.         // release the presentation space
  333.         WinReleasePS(hps);
  334.         break;
  335.     case WM_PAINT:
  336.         // just validate the update area. all drawing is done
  337.         // in the drawing-thread.
  338.         return WinDefWindowProc(hwnd, msg, mp1, mp2);
  339.     }
  340.     return WinDefWindowProc(hwnd, msg, mp1, mp2);
  341. }
  342.  
  343. // !!!!! the following function was added since version 1.0
  344. /*
  345.     priority_thread
  346.     This thread controls the priority of the drawing thread.
  347.     With these changes, if a saver module runs on low priority (this is
  348.     the default setting), it rises to normal priority twice a second
  349.     for 0.1 seconds. This should solve the problem that, when very
  350.     time-consuming processes were running, the module seemed not to become
  351.     active at all (in fact it became active, but did not get enough CPU
  352.     time to do its saver action).
  353.     There should be no reason to alter the code.
  354. */
  355. void    priority_thread(void *args)
  356. {
  357.     DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, 0, 0);
  358.     for(;;){
  359.         DosSetPriority(PRTYS_THREAD, PRTYC_REGULAR, 0, tidDraw);
  360.         DosSleep(100);
  361.         DosSetPriority(PRTYS_THREAD, PRTYC_IDLETIME, 0, tidDraw);
  362.         DosSleep(400);
  363.     }
  364. }
  365.  
  366. /*
  367.     load_configuration_data
  368.     Load all module configuration data from the INI-file into the
  369.     configuration_data structure, if not done already loaded.
  370. */
  371. void    load_configuration_data(void)
  372. {
  373.     if(configuration_data_loaded == FALSE){
  374.         // data not loaded yet
  375.         ULONG    size;
  376.         BOOL    fSuccess;
  377.         // get name of the saver module (stored as resource string)
  378.         if(WinLoadString(hab, hmodDLL, IDS_MODULENAME,
  379.           SAVER_NAME_MAXLEN, (PSZ)modulename) == 0){
  380.             // resource string not found. indicate error by
  381.             // setting module name to empty string (the name
  382.             // "" is interpreted as an error by SSDLL.DLL and
  383.             // the module does not show up in the list box).
  384.             strcpy(modulename, "");
  385.             return;
  386.         }
  387.         // load data from INI-file. the key name is the name of the
  388.         // saver module
  389.         size = sizeof(configuration_data);
  390.         fSuccess = PrfQueryProfileData(HINI_USER,
  391.           (PSZ)application_name, (PSZ)modulename,
  392.           (PSZ)&configuration_data, &size);
  393.         if(!fSuccess || size != sizeof(configuration_data) || configuration_data.version != MODULEVERSION){
  394.             // if entry is not found or entry has invalid size or
  395.             // entry has wrong version number, create a new entry
  396.             // with default values and write it to the INI-file
  397.             configuration_data.version = MODULEVERSION;
  398.             configuration_data.enabled = TRUE;
  399. /*
  400.             $$$$$ insert code here $$$$$
  401.             If you have added data to the configuration_data
  402.             structure, insert code here to set the default
  403.             values for your data items.
  404.  
  405.             $$$$$ for example $$$$$
  406.             configuration_data.count = CONFIGURATION_DEFAULT_COUNT;
  407. */
  408.             PrfWriteProfileData(HINI_USER, (PSZ)application_name, (PSZ)modulename, (PSZ)&configuration_data, sizeof(configuration_data));
  409.         }
  410.         configuration_data_loaded = TRUE;
  411.     }
  412. }
  413.  
  414. /*
  415.     draw_thread
  416.     This is the drawing-thread.
  417.     You have a valid presentation space handle (hps), a valid window
  418.     handle (hwndSaver) and the configuration_data structure is loaded.
  419.     The screen size is stored in "screenSizeX" and "screenSizeY".
  420.     IMPORTANT NOTE 1:
  421.     You must check the "stop_draw_thread" flag regularly. If it is set,
  422.     free all resources you have allocated and call _endthread (or just
  423.     return) to end the drawing-thread.
  424.     IMPORTANT NOTE 2:
  425.     If the "low_priority" flag is NOT set (that means you run with
  426.     regular priority, sharing CPU usage with other programs), you should
  427.     call DosSleep(x) with "x" set at least to 1 as often as possible, to
  428.     allow other programs to do their work. A screen saver should not eat
  429.     up other program's CPU time!
  430.     IMPORTANT NOTE 3:
  431.     For some of the PM calls to work properly, your thread needs an
  432.     own HAB and maybe even a message queue. You have to get and release
  433.     both of them here if you use those PM calls.
  434.  
  435.     The following sample code is from the "Pyramids" module that comes
  436.     with the ScreenSaver distribution.
  437.     It selects a random color and a random point on the screen, then
  438.     draws lines in the selected color from each corner of the screen
  439.     to the selected point (looks somewhat like a pyramid).
  440.     It remembers a number of points (this number can be set in the
  441.     configuration dialog). Having filled the point memory, it redraws
  442.     the "oldest" visible pyramid in black. This has the effect that more
  443.     and more pixels on the screen get black, only a few constantly
  444.     changing colored lines remain.
  445. */
  446. void    draw_thread(void *args)
  447. {
  448. /*
  449.     $$$$$ replace or change the following example code $$$$$
  450.     int    i, j;
  451.     POINTL    pt_corner[4];
  452.     BOOL    point_buffer_filled;
  453.     POINTL    *pt;
  454.  
  455. //    HAB    drawingthread_hab = WinInitialize(0);
  456. //    HMQ    drawingthread_hmq = WinCreateMsgQueue(drawingthread_hab, 0);
  457.  
  458.     i = 0;
  459.     point_buffer_filled = FALSE;
  460.     // allocate stack memory for circular point buffer
  461.     pt = alloca(configuration_data.count * sizeof(POINTL));
  462.  
  463.     // set the corner coordinates
  464.     pt_corner[0].x = 0;
  465.     pt_corner[0].y = 0;
  466.     pt_corner[1].x = 0;
  467.     pt_corner[1].y = screenSizeY-1;
  468.     pt_corner[2].x = screenSizeX-1;
  469.     pt_corner[2].y = 0;
  470.     pt_corner[3].x = screenSizeX-1;
  471.     pt_corner[3].y = screenSizeY-1;
  472.  
  473.     while(!stop_draw_thread){
  474.         if(point_buffer_filled){
  475.             // redraw old pyramid in black
  476.             GpiSetColor(hps, CLR_BLACK);
  477.             for(j=0;j<4;j++){
  478.                 GpiMove(hps, &pt[i]);
  479.                 GpiLine(hps, &pt_corner[j]);
  480.             }
  481.         }
  482.  
  483.         // select random color
  484.         GpiSetColor(hps, ((unsigned)rand()) % 16);
  485.  
  486.         // select random point and store it in buffer
  487.         pt[i].x = ((unsigned)rand()) % screenSizeX;
  488.         pt[i].y = ((unsigned)rand()) % screenSizeY;
  489.  
  490.         // draw pyramid
  491.         for(j=0;j<4;j++){
  492.             GpiMove(hps, &pt[i]);
  493.             GpiLine(hps, &pt_corner[j]);
  494.         }
  495.  
  496.         // move circular buffer index
  497.         i++;
  498.         if(i == configuration_data.count)
  499.             point_buffer_filled = TRUE;
  500.         i %= configuration_data.count;
  501.  
  502.         // sleep if necessary
  503.         if(low_priority == FALSE)
  504.             DosSleep(1);
  505.     }
  506.     // free resources
  507.  
  508. //    WinDestroyMsgQueue(drawingthread_hmq);
  509. //    WinTerminate(drawingthread_hab);
  510. */
  511. }
  512.